/*
 * Copyright (c) 2011 Ball State University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package edu.bsu.android.gtv.apps.bconnected.ui;

import java.io.IOException;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.http.HttpResponse; 
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import edu.bsu.android.gtv.apps.bconnected.util.UIUtils;
import edu.bsu.android.gtv.apps.bconnected.util.Crypto;
import edu.bsu.android.gtv.apps.bconnected.R;
import edu.bsu.android.gtv.apps.bconnected.data.DBAdapter;
import edu.bsu.android.gtv.apps.bconnected.objects.Person;
import edu.bsu.android.gtv.apps.bconnected.parser.PersonHandler;
import edu.bsu.android.gtv.apps.bconnected.ui.AboutMeActivity;
import edu.bsu.android.gtv.apps.bconnected.ui.ConnectionMultiPaneActivity;
import edu.bsu.android.gtv.apps.bconnected.ui.CourseMultiPaneActivity;
import edu.bsu.android.gtv.apps.bconnected.ui.HousingInfoActivity;
import edu.bsu.android.gtv.apps.bconnected.ui.OrganizationMultiPaneActivity;
import edu.bsu.android.gtv.apps.bconnected.util.AnalyticsUtils;
import edu.bsu.android.gtv.apps.bconnected.util.BSUPrivate;
import edu.bsu.android.gtv.apps.bconnected.util.Constants;
import edu.bsu.android.gtv.apps.bconnected.util.DisclaimerHelper;
import edu.bsu.android.gtv.apps.bconnected.util.LoginHelper;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;

/**
 * Handles the authentication for the application. This activity uses an HTTP connection to authenticate the user's credentials 
 * against a Windows-based auth site on a .NET web server.  A simple XML feed is returned after a successful login containing 
 * the BSU ID and login guid which will be used throughout the application as a "token" to ensure the user is retrieving the 
 * correct data from the service handlers
 * @author Kyle Parker, University Computing Services
 * 
 * NOTES:
 * After the log in, save the BSU ID to the database with the expiration date (current date + 1 hour)
 * Throughout the app, retrieve the BSU ID and check that the expiration date is not greater than the current date
 * If the expiration date is less than the current date, send the user to the login screen, then update the expiration date
 * All screens will need to check and redirect if necessary
 * There will only be 1 row in the database for the student - handled by an initial insert, with subsequent updates
 * Provide a "log out" option that will set the expiration date to the current date and therefor prompt for a login on the next visit
*/
// NOT IMPLEMENTED: Should this be a dialog fragment instead of an activity?
// NOTE: While the book says data i/o can be costly and slow on some devices, not sure how to wrap this in a thread and not lose track of things
// TODO: Potential added security layer - user generated secret keys. When the student logs in, generate a key, store in SQLite and SQL Server, use that
// for each session to encrypt/decrypt the data.
public class LoginActivity extends Activity {
    private static final String TAG = "LoginActivity";

	private int mDestination;
	private Activity mActivity;
    private ProgressDialog mProgressDialog = null; 
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        
        mActivity = this;
        
        AnalyticsUtils.getInstance(this).trackPageView("/Login");	                
		getExtras();   
        
        // The person has logged in, so continue on to the next screen
        if (LoginHelper.hasLoggedIn(mActivity)) {
        	processActivity();
        } else {
            // Display a text entry dialog 
            showDialog(Constants.DIALOG_TEXT_ENTRY);
        }
    }

	/**
     * Get the extras from the Intent
	 */
	private void getExtras() {	    	
		Bundle extras = getIntent().getExtras();   
		if (extras != null) {
			if (extras.getString(Constants.EXTRA_DESTINATION_FRAGMENT) != null) {
				mDestination = Integer.parseInt(extras.getString(Constants.EXTRA_DESTINATION_FRAGMENT));
			}
		}	
	}
	
	/**
	 * Based on the destination, pass control to proper activity
	 */
	private void processActivity() {
    	switch (mDestination) {
        	case Constants.DESTINATION_ABOUT_ME:
                startActivity(new Intent(mActivity, AboutMeActivity.class)); 
        		break;
        	case Constants.DESTINATION_SCHEDULE:
        		startActivity(new Intent(mActivity, CourseMultiPaneActivity.class));
        		break;
        	case Constants.DESTINATION_STUDENT_ORGS:
                startActivity(new Intent(mActivity, OrganizationMultiPaneActivity.class)); 
        		break;
        	case Constants.DESTINATION_MY_HOME:
                startActivity(new Intent(mActivity, HousingInfoActivity.class));  
        		break;
        	case Constants.DESTINATION_MY_CONNECTIONS:
                startActivity(new Intent(mActivity, ConnectionMultiPaneActivity.class));  
        		break;
        	default:
                startActivity(new Intent(mActivity, HomeActivity.class));  
        		break;
    	} 
    	
        finish();
	}
	
    @Override
    protected Dialog onCreateDialog(int id) {
	    LayoutInflater factory = LayoutInflater.from(mActivity);
	    final View root = factory.inflate(R.layout.dialog_login, null);
	    	    
	    return new AlertDialog.Builder(mActivity)
	        .setIcon(R.drawable.ic_dialog_alert) 
	        .setTitle(R.string.dialog_bsu_login) 
	        .setView(root)
	        .setPositiveButton(R.string.dialog_ok, new DialogInterface.OnClickListener() {
	            public void onClick(DialogInterface dialog, int whichButton) {
                    Runnable load = new Runnable(){
                        public void run() {
                        	try {	        	                    
        		            	handleLogin(root);
        		            	finish();
                        	} catch (Exception ex) {
        		                mProgressDialog.dismiss();

        	                    AnalyticsUtils.getInstance(mActivity).trackPageView(TAG + ": " + ex.getMessage());
        	                    
        		                final Intent intent = new Intent(Intent.ACTION_VIEW, null, mActivity, HomeActivity.class);
        		                intent.putExtra(Constants.EXTRA_LOGIN_STATUS, Integer.toString(Constants.LOGIN_STATUS_FAILED));
        		                
        		                startActivity(intent); 
        		                finish();  
                        	} 
                        }
                    };
                    
                    Thread thread =  new Thread(null, load, "handleLogin");
                    thread.start(); 
                    mProgressDialog = ProgressDialog.show(mActivity, 
                    		mActivity.getString(R.string.dialog_bsu_login), 
                    		mActivity.getString(R.string.dialog_login_message), true);	
	            }
	        })
	        .setNegativeButton(R.string.dialog_cancel, new DialogInterface.OnClickListener()  {
	            public void onClick(DialogInterface dialog, int whichButton) {	
	                final Intent intent = new Intent(Intent.ACTION_VIEW, null, mActivity, HomeActivity.class);
	                intent.putExtra(Constants.EXTRA_LOGIN_STATUS, Integer.toString(Constants.LOGIN_STATUS_CANCELLED));
	                
	                startActivity(intent);  
	                finish();
	            }
	        })
	        .create();
    }
    
    /**
     * Process the login credentials entered by the student
     * This authenticates against a .NET web application with basic authentication/windows authentication enabled
     * If the login is successful, a basic data set will be saved to a local database for future authentication checks
     * @param view
     */
    protected void handleLogin(View view) {
        // Create credentials for basic auth 
        EditText username = (EditText)view.findViewById(R.id.edit_username);
        EditText password = (EditText)view.findViewById(R.id.edit_password);
        UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
        		username.getText().toString().trim().toLowerCase(), 
        		password.getText().toString().trim());  
        
        if (username.getText().toString().toLowerCase().equals(BSUPrivate.VALUE_DEMO_ACCOUNT)) {
        	processDemoAccount();
        } else {
            processBasicAuth(credentials);
        }
    }

    /**
     * Process the login credentials entered by the student
     * This authenticates against a .NET web application with basic authentication/windows authentication enabled
     * If the login is successful, a basic data set will be saved to a local database for future authentication checks
     * @param credentials
     */
    protected void processBasicAuth(UsernamePasswordCredentials credentials) {
        // Authenticate against the basic auth on the server - this is different than the forms based authentication used for .NET
        // Would need to create a basic auth app on a server to execute the database lookup based on the credentials and retrieve the BSU ID
        // First create defaulthttpclient 
        DefaultHttpClient httpclient = new DefaultHttpClient();
        
        // create a basic credentials provider and pass the credentials 
        BasicCredentialsProvider bcp = new BasicCredentialsProvider(); 
        bcp.setCredentials(AuthScope.ANY, credentials); 
                
        // Set credentials provider for our default http client so it will use those credentials 
        httpclient.setCredentialsProvider(bcp); 

        HttpGet httpget = new HttpGet(BSUPrivate.LOGIN_URL + BSUPrivate.APPLICATION_GUID);

        HttpResponse response = null;
		try {
			response = httpclient.execute(httpget);
		} catch (ClientProtocolException ex) {
			Log.e("LoginActivity", ex.getMessage());
		} catch (IOException ex) {
			Log.e("LoginActivity", ex.getMessage());
		}
		
        int statusCode = response.getStatusLine().getStatusCode(); 

        if (statusCode == Constants.HTTP_OK) {
        	InputStream is = null;
			try {
				is = response.getEntity().getContent();
			} catch (IllegalStateException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} catch (IOException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} 
        
	        // Create a SAXParser to parse the XML file 
	        SAXParserFactory spf = SAXParserFactory.newInstance();  
	        SAXParser sp = null;
			try {
				sp = spf.newSAXParser();
			} catch (ParserConfigurationException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} catch (SAXException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} 
	        XMLReader xr = null;
			try {
				xr = sp.getXMLReader();
			} catch (SAXException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} 
	        
	        // Set a handler to parse the XML 
	        PersonHandler handler = new PersonHandler(); 
	        xr.setContentHandler(handler); 
	        try {
				xr.parse(new InputSource(is));
			} catch (IOException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} catch (SAXException ex) {
				Log.e("LoginActivity", ex.getMessage());
			} 
	        
	        // Retrieve result from the parser 
	        Person person = handler.getParsedData(); 
	        
	        if (person != null) {  
	        	handleAuth(person, false);
	        }
	        
        	processActivity();   
        } else {        	
            Intent intent = new Intent(Intent.ACTION_VIEW, null, mActivity, HomeActivity.class);
            intent.putExtra(Constants.EXTRA_LOGIN_STATUS, Integer.toString(Constants.LOGIN_STATUS_FAILED));

            startActivity(intent);  
            finish();
        }

        // When HttpClient instance is no longer needed, shut down the connection manager to ensure immediate deallocation of all system resources
        httpclient.getConnectionManager().shutdown(); 
        mProgressDialog.dismiss();  
    }

    /**
     * Process the demo account login and set the default values for the account
     */
    protected void processDemoAccount() {
		Crypto c = new Crypto(BSUPrivate.KEY);

    	Person person = new Person();
    	person.setBSUID(c.encryptAsBase64(BSUPrivate.VALUE_DEMO_BSU_ID.getBytes()));
    	person.setLoginGuid(UUID.randomUUID().toString());
    	person.setFirstName(BSUPrivate.VALUE_DEMO_FIRST_NAME);

    	handleAuth(person, true);
    	UIUtils.resetAllIDs(mActivity);

    	processActivity(); 
        mProgressDialog.dismiss();  
    }

	/**
	 * Handle the insert or update of the authentication credentials
	 * @param person
	 * @param demoAccount
	 */
    protected void handleAuth(Person person, boolean demoAccount) {
    	Calendar cal = Calendar.getInstance();
    	// Set the expiration date to one hour from the current day/time
		cal.add(Calendar.HOUR, 1);
    	DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 

    	String loginGuid = person.getLoginGuid();

        DBAdapter db = new DBAdapter(this);
        db.open();
	    
	    // Retrieve the user information based on the BSU ID
        Cursor data = db.getSecurityByBSUID(person.getBSUID());
        
        startManagingCursor(data);
        
        // If there is data in the cursor, determine the expiration date
        if (data.getCount() > 0) { 
        	// Retrieve the values from the cursor
        	int rowID = Integer.parseInt(data.getString(data.getColumnIndexOrThrow(DBAdapter.KEY_ROWID)));
            Date expirationDate = Calendar.getInstance().getTime();
            Date todayDate = Calendar.getInstance().getTime(); 
            
			try {
				expirationDate = df.parse(data.getString(data.getColumnIndexOrThrow(DBAdapter.KEY_EXPIRATION_DATE)));
			} catch (IllegalArgumentException ex) {
				Log.e("LoginActivity.processBasicAuth", ex.getMessage());
			} catch (ParseException ex) {
				Log.e("LoginActivity.processBasicAuth", ex.getMessage());
			}
            
            // Determine if the expiration date is valid
            if (expirationDate.before(todayDate)) {
    		    // Update the expiration date based on the most recent login
    		    db.updateSecurity(rowID, loginGuid, person.getBSUID(), cal.getTime());
            } 
            
            // If the expiration date is valid, continue - nothing needs to be done
        }  else {
        	db.insertSecurity(loginGuid, person.getBSUID(), person.getFirstName(), cal.getTime()); 
        	DisclaimerHelper.setAcceptedDisclaimer(mActivity, false);
        }
        
        LoginHelper.setLogin(mActivity, true);
        LoginHelper.setDemoAccount(mActivity, demoAccount);
        
        stopManagingCursor(data);
        //data.close();
        db.close();		  
    }
}
